from flask import Flask, render_template, send_from_directory, jsonify, send_file, request
from werkzeug.utils import safe_join
import os
from urllib.parse import quote, unquote
import mimetypes

app = Flask(__name__)

app.config['HEADERS_USE_UTF8'] = True

# 这里替换成你自己储存视频的总文件夹
MOVIES_FOLDER = os.path.abspath(os.path.join(app.root_path, r"/Movies"))

def is_safe_path(basedir, path):
    # 检查路径是否在基础目录内
    basedir = os.path.abspath(basedir)
    path = os.path.abspath(path)
    return path.startswith(basedir)

def get_video_files(directory):
    video_files = []
    directory = os.path.abspath(directory)  # 确保使用绝对路径

    for root, dirs, files in os.walk(directory):
        # 跳过隐藏目录
        dirs[:] = [d for d in dirs if not d.startswith('.')]

        relative_root = os.path.relpath(root, directory).replace("\\", "/")
        if relative_root == ".":
            relative_root = ""

        for file in files:
            # 跳过隐藏文件
            if file.startswith('.'):
                continue

            if file.lower().endswith(('.mp4', '.avi', '.mkv', '.mov')): #允许的文件扩展名
                video_path = f"{relative_root}/{file}".lstrip('/')
                video_id = quote(video_path)
                video_files.append({
                    'id': video_id,
                    'name': file,
                    'folder': relative_root,
                    'size': os.path.getsize(os.path.join(root, file))
                })
    return video_files


def send_video_file(full_path, video_path):
    # 安全的发送视频文件
    response = send_from_directory(
        os.path.dirname(full_path),
        os.path.basename(full_path),
        as_attachment=False
    )
    response.headers['Content-Type'] = mimetypes.guess_type(full_path)[
        0] or 'video/mp4'
    response.headers[
        'Content-Disposition'] = f"inline; filename*=UTF-8''{quote(os.path.basename(full_path))}"
    response.headers['Accept-Ranges'] = 'bytes'
    return response

@app.route('/')
def index():
    return render_template('index.html')

@app.route('/videos/')
def list_videos():
    folder = request.args.get('folder', '')
    videos = get_video_files(MOVIES_FOLDER)
    if folder:
        videos = [video for video in videos if video['folder'] == folder]
    return jsonify(videos)

@app.route('/play')
def play():
    return render_template('play.html')


@app.route('/videos/<path:video_path>')
def video(video_path):
    try:
        # 解码URL编码的路径
        video_path = unquote(video_path)

        # 检查路径中是否包含可疑字符
        if any(char in video_path for char in ['..', '~', '//']):
            return "Invalid path", 400

        # 构建完整路径并验证
        full_path = safe_join(MOVIES_FOLDER, video_path)
        if full_path is None:
            return "Invalid path", 400

        # 额外的路径安全检查
        if not is_safe_path(MOVIES_FOLDER, full_path):
            return "Access denied", 403

        # 验证文件存在且是允许的类型
        if not os.path.isfile(full_path):
            return "File not found", 404

        allowed_extensions = ('.mp4', '.avi', '.mkv', '.mov')
        if not full_path.lower().endswith(allowed_extensions):
            return "File type not allowed", 403

        # 发送文件
        return send_video_file(full_path, video_path)

    except Exception as e:
        app.logger.error(f"Error accessing file: {str(e)}")
        return "Internal server error", 500

@app.before_request
def check_request():
    # 防止路径遍历攻击的常见模式
    if '..' in request.path or '%2e%2e' in request.path.lower():
        return "Invalid request", 400

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5001, debug=False)
